﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using NLog;

namespace PluginDB
{
    public static class PagingExtensions
    {
        //used by LINQ to SQL
        public static IQueryable<TSource> Page<TSource>(this IQueryable<TSource> source, int page, int pageSize)
        {
            return source.Skip((page - 1) * pageSize).Take(pageSize);
        }

        //used by LINQ
        public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int page, int pageSize)
        {
            return source.Skip((page - 1) * pageSize).Take(pageSize);
        }
    }

    public static class StringExtensions
    {
        public static bool StartsWithAny(this string s, string items)
        {
            return items.StartsWith(s);
        }
    }

    public class TVShowsRepository
    {
        private readonly Logger _log = LogManager.GetLogger("TVShowsRepository");

        private readonly Repository<TVShow, PluginDbEntities> _tvShowRepository;
        private readonly Repository<TVShowsLink, PluginDbEntities> _tvShowsLinkRepository;
        private readonly Repository<Series, PluginDbEntities> _seriesRepository;
        private readonly CommonRepository _commonRepository;

        private readonly Repository<ValidSeriesView, PluginDbEntities> _validSeriesViewRepository;
        private readonly Repository<ValidTVShowsView, PluginDbEntities> _validTVShowsViewRepository;

        public TVShowsRepository(PluginDbEntities pluginDbEntities)
        {
            _tvShowRepository = new Repository<TVShow, PluginDbEntities>(pluginDbEntities);
            _tvShowsLinkRepository = new Repository<TVShowsLink, PluginDbEntities>(pluginDbEntities);
            _seriesRepository = new Repository<Series, PluginDbEntities>(pluginDbEntities);

            _validSeriesViewRepository = new Repository<ValidSeriesView, PluginDbEntities>(pluginDbEntities);
            _validTVShowsViewRepository = new Repository<ValidTVShowsView, PluginDbEntities>(pluginDbEntities);


            _commonRepository = new CommonRepository(pluginDbEntities);
        }

        public bool IsSeriesExists(string seriesName)
        {
            return _seriesRepository.DoQuery().Any(a => a.Name.ToLower().Equals(seriesName.ToLower()));
        }

        public bool IsTVShowExists(string pluginUrl)
        {
            return _tvShowRepository.DoQuery(
                new Specification<TVShow>(v => v.PluginUrl.ToLower() == pluginUrl.ToLower())).Any();
        }

        public Series CreateSeries(string name)
        {
            var series = new Series
            {
                Name = name,
            };

            _seriesRepository.Add(series);
            return series;
        }

        public Series GetSeries(long seriesId)
        {
            return _seriesRepository.DoQuery(new Specification<Series>(a => a.SeriesID == seriesId)).FirstOrDefault();
        }

        public Series GetSeries(string name)
        {
            return _seriesRepository.DoQuery(
                new Specification<Series>(a => a.Name.ToLower().Equals(name.ToLower()))).FirstOrDefault() ??
                           CreateSeries(name);
        }

        public void AddTVShow(PluginVideoInfo pluginVideoInfo)
        {
            try
            {
                var tvShow = new TVShow
                {
                    VideoID = pluginVideoInfo.VideoID,
                    Name = pluginVideoInfo.VideoName,
                    EnglishName = pluginVideoInfo.EnglishVideoName,
                    HebrewName = pluginVideoInfo.HebrewVideoName,
                    ImageUrl = pluginVideoInfo.ImageUrl,
                    PluginUrl = pluginVideoInfo.PluginUrl,
                    Site = _commonRepository.GetSite(pluginVideoInfo.SiteName),
                    SeasonNumber = pluginVideoInfo.TVShowInformation.SeasonNumber,
                    EpisodeNumber = pluginVideoInfo.TVShowInformation.EpisodeNumber,
                    IsHebSub = pluginVideoInfo.IsHebSub
                };

                //Handle links
                foreach (var link in pluginVideoInfo.Links)
                {
                    if (link.IsBrokenLink) continue;

                    tvShow.TVShowsLinks.Add(new TVShowsLink
                    {
                        LinkURL = link.LinkURL,
                        Source = _commonRepository.GetSource(link.SourceName),
                        IsValid = true,
                        ValidDate = DateTime.Now
                    });
                }

                //Handle Series
                var series = GetSeries(pluginVideoInfo.TVShowInformation.SeriesName);
                if (series.SeriesID == 0 && pluginVideoInfo.TVShowInformation.TVDBInformationItem.IsValid)
                {
                    series.FanartUrl = pluginVideoInfo.TVShowInformation.TVDBInformationItem.FanartUrl;
                    series.ImageUrl = pluginVideoInfo.TVShowInformation.TVDBInformationItem.FanartUrl;
                    series.Plot = pluginVideoInfo.TVShowInformation.TVDBInformationItem.Plot;
                }
                tvShow.Series = series;

                //If there is no VALID link in video then dont save it
                if (tvShow.TVShowsLinks.Count == 0)
                {
                    //_log.WarnFormat("TVShow:{0} has no valid links. abort saving it.", tvShow.Name);
                    return;
                }

                _tvShowRepository.Add(tvShow);
                _tvShowRepository.Save();
            }
            catch (UpdateException ex)
            {
                Console.WriteLine("UpdateException durin update. Ex={0}", ex.InnerException.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception durin Save. Ex={0}", ex.InnerException.Message);
            }
        }

        public List<ValidTVShowsView> GetLatestVideosList(int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                                                                .ValidTVShowsViews
                                                                .OrderByDescending(t=>t.VideoID)
                                                                .ToList();
            return validLatestTVShows.Page(page, pageSize).ToList();
        }

        public List<ValidSeriesView> GetHebSubVideos(int page, int pageSize)
        {
            var validSeriesViews = _validSeriesViewRepository.Session
                                                                .ValidSeriesViews
                                                                .Where(t => t.IsHebSub.HasValue && t.IsHebSub.Value)
                                                                .OrderBy(s => s.Name)
                                                                .ToList();
            return validSeriesViews.Page(page, pageSize).ToList();
        }

        internal List<ValidTVShowsView> GetTVShowsByCategoryId(long categoryId, int page, int pageSize)
        {
            throw new NotImplementedException("GetTVShowsByCategoryId Not Implemented yet");

            //return _tvShowRepository.Session.TVShows
            //        .Where(v => (v.Categories.Where(c => c.CategoryID == categoryId)).Count() > 0).Page(page, pageSize).ToList();
        }

        internal List<ValidSeriesView> GetAtoZSeriesList(string startWord, bool isHebWord, int page, int pageSize)
        {
            var validSeriesViews = _validSeriesViewRepository.Session
                .ValidSeriesViews
                .OrderBy(s => s.Name).ToList();

            return validSeriesViews.Where(s => s.Name.StartsWith(startWord)).Page(page, pageSize).ToList();
        }

        internal List<ValidSeriesView> GetTVShowsBySite(long siteId, int page, int pageSize)
        {
            var validSeriesViews = _validSeriesViewRepository.Session
                                                            .ValidSeriesViews
                                                            .Where(s => s.SiteID == siteId)
                                                            .OrderBy(s => s.Name)
                                                            .ToList();
            return validSeriesViews.Page(page, pageSize).ToList();
        }

        public List<ValidSeriesView> GetSeriesBySeriesId(long seriesId)
        {
            var validSeriesViews = _validSeriesViewRepository.Session
                                                            .ValidSeriesViews
                                                            .Where(s => s.SeriesID == seriesId)
                                                            .OrderBy(s => s.Name)
                                                            .ToList();
            return validSeriesViews;
        }

        internal TVShow GetTVShowByID(long videoId)
        {
            var video = _tvShowRepository.DoQuery(
                new Specification<TVShow>(v => v.VideoID == videoId)).Include("TVShowsLinks").FirstOrDefault();
            return video;
        }

        internal TVShowsLink GetTVShowLinkByID(long linkId)
        {
            var link = _tvShowsLinkRepository.DoQuery(
                new Specification<TVShowsLink>(v => v.LinkID == linkId)).Include("TVShow").FirstOrDefault();
            return link;
        }

        internal List<ValidSeriesView> GetSeriesList(int page, int pageSize)
        {
            var validSeriesViews = _validSeriesViewRepository.Session
                                                                .ValidSeriesViews
                                                                .OrderBy(s => s.Name)
                                                                .ToList();
            return validSeriesViews.Page(page, pageSize).ToList();
        }

        internal List<ValidTVShowsView> GetTVShowsBySeries(long seriesId, int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                                                                .ValidTVShowsViews
                                                                .Where(t => t.SeriesID == seriesId)
                                                                .OrderBy(s => s.Name)
                                                                .ToList();
            return validLatestTVShows.Page(page, pageSize).ToList();
        }

        internal List<long> GetHebSubSeasonsbySeriesId(long seriesId, int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                                                                .ValidTVShowsViews
                                                                .Where(t => t.SeriesID == seriesId && t.IsHebSub.HasValue && t.IsHebSub == true)
                                                                .Select(s => s.SeasonNumber)
                                                                .Distinct();
            return validLatestTVShows.ToList();
        }

        internal List<long> GetSeasonsbySeriesAndSiteId(long siteId, long seriesId, int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                                                                .ValidTVShowsViews
                                                                .Where(t => t.SeriesID == seriesId && t.SiteID == siteId)
                                                                .Select(s=>s.SeasonNumber)
                                                                .Distinct();
            return validLatestTVShows.ToList();
        }

        internal List<long> GetSeasonsbySeriesId(long seriesId, int page, int pageSize)
        {
            var query = _validTVShowsViewRepository.Session
                .ValidTVShowsViews
                .Where(t => t.SeriesID == seriesId)
                .Select(s => s.SeasonNumber)
                .Distinct();
            return query.ToList();
        }

        internal List<ValidTVShowsView> GetHebSubTVShowsBySeasonId(long seriesId, long seasonNumber, int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                .ValidTVShowsViews
                .Where(t => t.SeriesID == seriesId && t.IsHebSub.HasValue && t.IsHebSub.Value)
                .OrderBy(s => s.EpisodeNumber);

            return validLatestTVShows.Page(page, pageSize).ToList();
        }

        internal List<ValidTVShowsView> GetTVShowsBySeasonAndSiteId(long siteId, long seriesId, long seasonNumber, int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                                                              .ValidTVShowsViews
                                                              .Where(t => t.SeriesID == seriesId && 
                                                                            t.SiteID == siteId &&
                                                                            t.SeasonNumber == seasonNumber)
                                                              .OrderByDescending(s => s.EpisodeNumber)
                                                              .ToList();
            return validLatestTVShows.Page(page, pageSize).ToList();
        }

        internal List<ValidTVShowsView> GetTVShowsBySeasonId(long seriesId, long seasonNumber, int page, int pageSize)
        {
            var validLatestTVShows = _validTVShowsViewRepository.Session
                                                              .ValidTVShowsViews
                                                              .Where(t => t.SeriesID == seriesId && t.SeasonNumber == seasonNumber)
                                                              .OrderByDescending(s => s.EpisodeNumber)
                                                              .ToList();

            return validLatestTVShows.Page(page, pageSize).ToList();
        }

        internal List<ValidSeriesView> GetTVShowsBySearchText(string searchText, int page, int pageSize)
        {
            var validSeriesViews = _validSeriesViewRepository.Session
                                                               .ValidSeriesViews
                                                               .Where(s => s.Name.ToLower().Contains(searchText.ToLower()))
                                                               .OrderBy(s => s.Name)
                                                               .ToList();
            return validSeriesViews.Page(page, pageSize).ToList();
        }

        internal List<TVShowsLink> GetAllTVShowsLinks()
        {
            return _tvShowsLinkRepository.Session.TVShowsLinks.ToList();
        }

        internal void SaveAllTVShows()
        {
            _tvShowsLinkRepository.Save();
            _tvShowRepository.Save();
        }

        internal List<Series> GetAllSeries()
        {
            return _seriesRepository.SelectAll().ToList();
        }

        internal List<Series> GetSeriesByName(string seriesName)
        {
            return _seriesRepository.Session.Series.Where(x=>x.Name.Equals(seriesName)).ToList();
        }

        internal void DeleteSeries(Series series)
        {
            _seriesRepository.Delete(series);
        }

        internal void SaveAllSeries()
        {
            _seriesRepository.Save();
        }

        internal List<TVShow> GetTVShowsBySeriesId(long seriesId)
        {
            return _tvShowRepository.Session.TVShows.Where(x => x.Series.SeriesID == seriesId).ToList();
        }

        internal Series GetSeriesById(long seriesId)
        {
            return _seriesRepository.Session.Series.Where(x => x.SeriesID == seriesId).FirstOrDefault();
        }

        internal List<Series> GetSeriesByUserId(long userId)
        {
            return _seriesRepository.Session.Series.Where(s => s.Users.Any(u => u.UserID == userId)).ToList();
        }

        internal bool IsUserSeriesExists(long userId, long seriesId)
        {
            return _seriesRepository.Session.Series.Where(s => s.Users.Any(u => u.UserID == userId)).Any(x=>x.SeriesID == seriesId);
        }

        public long GetSeriesIdByVideoId(int videoId)
        {
            var video = _tvShowRepository.Session.TVShows.Include("Series").Where(t => t.VideoID == videoId).FirstOrDefault();
            if (video == null) throw new Exception("The video is not exists");

            return video.Series.SeriesID;
        }

        internal int GetSeriesCount()
        {
            return _validTVShowsViewRepository.Session.Series.Count();
        }

        internal int GetUserSeriesCount(long userID)
        {
            return _seriesRepository.Session.Series.Count(s => s.Users.Any(u => u.UserID == userID));
        }

        internal List<TVShowsLink> GetTVShowsLinksBySourceID(long sourceId)
        {
            return _tvShowRepository.Session.TVShowsLinks.Where(t => t.Source.SourceID == sourceId).ToList();
        }

        internal void DeleteTVShow(TVShow tVShow)
        {
            _tvShowRepository.Delete(tVShow);
            _tvShowRepository.Save();
        }

        internal void DeleteTVShowLink(TVShowsLink videoLink)
        {
            _tvShowsLinkRepository.Delete(videoLink);
            _tvShowsLinkRepository.Save();
        }

        internal List<TVShow> GetAllTVShows()
        {
             return _tvShowRepository.Session.TVShows.ToList();
        }
    }
}
